package com.hyzs.geomocker;

import com.alibaba.fastjson.JSON;
import com.hyzs.geomocker.bean.GeoPoints;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author yinchendong
 */
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
@RestController
@RequestMapping("geo")
@Api(tags = "kml转经纬度")
@Slf4j
public class GeomockerApplication {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    static final String COORDINATES_START = "<coordinates>";
    static final String COORDINATES_END = "</coordinates>";
    static final String GXCORD_START = "<gx:coord>";
    static final String GXCORD_END = "/<gx:coord>";

    public static void main(String[] args) {
        SpringApplication.run(GeomockerApplication.class, args);
    }

    /**
     * 两步路kml轨迹上传解析.
     *
     * @param file     两步路官网下载的kml格式文件
     * @return the
     * @author yinchendong
     * @date 2020年9月9日 上午11:17:15
     * @version 1.0
     */
    @PostMapping("upload")
    @ApiOperation("根据文件获取经纬度")
    public String upload(@ApiParam("kml轨迹文件") MultipartFile file) {
        if (file.isEmpty()) {
            return null;
        }
        // 整个文档结构
        String doc;
        try (InputStreamReader reader = new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8);
             BufferedReader br = new BufferedReader(reader)) {
            doc = br.lines().collect(Collectors.joining(""));
        }  catch (IOException e) {
            log.error("文件读取异常,", e);
            return null;
        }
        // 有缓存直接返回
        final String redisKey = "geo:points:" + file.getOriginalFilename();
        try (InputStreamReader reader = new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8);
             BufferedReader br = new BufferedReader(reader)) {
            List<String> result;
            if (doc.contains(GXCORD_START)) {
                // 适用于"<gx:coord>"标签解析
                result = br.lines()
                        .filter(line -> StringUtils.isNotBlank(line) && line.trim().startsWith(GXCORD_START))
                        .collect(Collectors.toList());
            } else {
                // 这个适用于<coordinates>格式解析，解析出来的结果只有一条
                result = br.lines()
                        .filter(line -> StringUtils.isNotBlank(line) && line.trim().startsWith(COORDINATES_START))
                        .collect(Collectors.toList());
            }
            if (result.size() == 0) {
                return null;
            }
            List<GeoPoints> results = new ArrayList<>();
            if (result.size() == 1) {
                // <coordinates>格式，只用一行存起来了
                String pointLine = String.join("", result);
                this.getPoints(results, pointLine);
            } else {
                for (String str : result) {
                    this.getPoints(results, str);
                }
            }
            log.info("数据总条数为：" + result.size());
            stringRedisTemplate.opsForValue().set(redisKey, JSON.toJSONString(results));
        } catch (IOException e) {
            log.error("文件读取异常,", e);
            return null;
        }
        return redisKey;
    }

    /**
     * 返回指定容易的经纬度坐标点信息.
     *
     * @param name the name
     * @param size the size
     * @return the geopoints
     * @author yinchendong
     * @date 2020年9月11日 上午11:22:37
     * @version 1.0
     */
    @GetMapping("points")
    @ApiOperation("根据文件获取经纬度")
    public List<GeoPoints> getGeopoints(@RequestParam @ApiParam("上传时返回的名称") String name,
            @RequestParam @ApiParam("返回的数字大小") Integer size) {
        List<GeoPoints> empty = new ArrayList<>();
        size = Optional.ofNullable(size).orElse(10);
        // 有缓存直接返回
        String value = stringRedisTemplate.opsForValue().get(name);
        if (StringUtils.isNotBlank(value)) {
            List<GeoPoints> geoPoints = JSON.parseArray(value, GeoPoints.class);
            int startIdx = Math.min(size, geoPoints.size());
            if (startIdx < size) {
                stringRedisTemplate.delete(name);
                return geoPoints.subList(0, startIdx);
            }
            stringRedisTemplate.opsForValue().set(name, JSON.toJSONString(geoPoints.subList(startIdx, geoPoints.size())));
            return geoPoints.subList(0, startIdx);
        }
        return empty;
    }

    private void getPoints(List<GeoPoints> results, String pointLine) {
        String line, oneSpace = " ";
        if (StringUtils.isNotBlank(pointLine) && pointLine.trim().startsWith(COORDINATES_START)) {
            line = pointLine.trim().substring(COORDINATES_START.length(), pointLine.length() - COORDINATES_END.length());
            if (line.contains(oneSpace)) {
                for (String point : line.split(oneSpace)) {
                    if (point.contains(",")) {
                        String[] arr = point.split(",");
                        GeoPoints geoPoints = new GeoPoints().setLng(arr[0]).setLat(arr[1]);
                        results.add(geoPoints);
                    }
                }
            }
        } else {
            line = pointLine.trim().substring(GXCORD_START.length(), pointLine.length() - GXCORD_END.length());
            if (line.contains(oneSpace)) {
                String[] arr = line.split(oneSpace);
                GeoPoints geoPoints = new GeoPoints().setLng(arr[0]).setLat(arr[1]);
                results.add(geoPoints);
            }
        }
    }

}
